package net.wwang.open.service.impl;

import net.wwang.open.dao.DocumentDao;
import net.wwang.open.dao.MenuDao;
import net.wwang.open.global.GlobalTipMsg;
import net.wwang.open.global.ResponseResult;
import net.wwang.open.pojo.Document;
import net.wwang.open.pojo.Menu;
import net.wwang.open.service.DocumentService;
import net.wwang.open.util.HtmlToPDFUtils;
import net.wwang.open.util.IDGenerateUtil;
import net.wwang.open.util.PropertiesUtil;
import net.wwang.open.util.Utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.jsoup.Jsoup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;


/**
 * @author WangWei
 * @date 2020/8/11
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class DocumentServiceImpl implements DocumentService {
	//默认根ID
	private static String ROOT_STATE_MENU_ID = "-1";
	//目录节点
	private static String MENU_TYPE_DEFAULT = "0";
	//文档节点
	private static String MENU_TYPE_DOCUMENT = "1";

	//保存文档位置
	@Value("${file.save-dir}")
	private String baseDir;

	@Autowired
	private MenuDao menuDao;
	@Autowired
	private DocumentDao documentDao;
	@Autowired
	private IDGenerateUtil idGenerator;
	@Autowired
	private DocumentService documentService;

	@Override
	public ResponseResult addDocument(String space, String parentId, Document document) throws IOException {

		if (Utils.isEmpty(parentId)) {
			return ResponseResult.FAILED(GlobalTipMsg.DOCUMENT_PARENT_NOT_EMPTY);
		}
		if (Utils.isEmpty(space)) {
			return ResponseResult.FAILED(GlobalTipMsg.MENU_SPACE_NOT_EMPTY);
		}
		//非目录禁止添加文档
		if (!parentId.equals(ROOT_STATE_MENU_ID)) {
			Menu parentMenu = menuDao.findMenuById(parentId);
			if (!parentMenu.getType().equals(MENU_TYPE_DEFAULT) && parentMenu != null) {
				return ResponseResult.FAILED(GlobalTipMsg.PROHIBIT_ADD_DOCUMENT);
			}
		}

		if (Utils.isEmpty(document.getContent())) {
			document.setContent("Seczone Opendocs");
		}
		//校验是否有重名文档
		List<Menu> menus = menuDao.findMenusByParentIdAndName(parentId, document.getTitle());
		if (menus.size() > 0) {
			return ResponseResult.FAILED(GlobalTipMsg.DOCUMENT_NAME_ALREADY_EXISTS).setData(document.getTitle());
		}
		Menu menu = new Menu();
		//设置文档节点数据
		menu.setType(MENU_TYPE_DOCUMENT);
		String menuId = String.valueOf(idGenerator.nextId());
		menu.setName(document.getTitle());
		menu.setId(menuId);
		menu.setParentId(parentId);
		menu.setSpace(space);
		//设置文档数据
		document.setMenuId(menuId);
		//获取保存文件的路径
		File file = new File(baseDir);
		Path dir = Paths.get(baseDir);
		if (!file.exists()) {
			Files.createDirectory(dir);
		}
		//文档空间预警
		long freeSpace = file.getFreeSpace();
		if (!Utils.checkFileSize(freeSpace, 500, "M")) {
			return ResponseResult.FAILED("剩余空间已不足500M，请清理一部分空间再来吧！");
		}
		//构建文档路径
		File documentFile = createDocumentFile(document, baseDir);
		if (documentFile == null) {
			log.error("文档存储到本地文件失败！");
			return ResponseResult.FAILED(GlobalTipMsg.DOCUMENT_SAVE_FAILED);
		}
		//设置doucument的内容为html的路径
		document.setContent(String.valueOf(documentFile.toPath()));
		//保存
		try {
			menuDao.save(menu);
			documentDao.save(document);
		} catch (Exception e) {
			log.error("文档保存到数据库失败，数据回滚!");
			FileUtils.forceDelete(documentFile);
			return ResponseResult.FAILED(GlobalTipMsg.DOCUMENT_SAVE_FAILED);
		}
		return ResponseResult.SUCCESS(GlobalTipMsg.DOCUMENT_SAVE_SUCCESS).setData(document);
	}

	private File createDocumentFile(Document document, String baseDir) throws IOException {
		Path docPath = Paths.get(baseDir, document.getTitle() + "_" + idGenerator.nextId() + ".html");
		File docfile = new File(String.valueOf(docPath));
		if (docfile.exists()) {
			FileUtils.forceDelete(docfile);
		}
		//创建文件并写入内容
		try {
			String content = document.getContent();
			Path path = Files.createFile(docPath);
			Files.write(path, content.getBytes("utf-8"));
			return docfile;
		} catch (IOException e) {
			FileUtils.forceDelete(docfile);
			return null;
		}
	}

	@Override
	public ResponseResult delDocument(String menuId) {
		Menu menu = menuDao.findMenuById(menuId);
		Document document = documentDao.findDocumentByMenuId(menuId);
		if (menu == null || document == null) {
			return ResponseResult.FAILED(GlobalTipMsg.DOCUMENT_NOT_EXISTS);
		}
		try {
			menuDao.deleteMenuById(menuId);
			documentDao.deleteDocumentByMenuId(menuId);
		} catch (Exception e) {
			return ResponseResult.FAILED(GlobalTipMsg.DOCUMENT_DELETE_FAILED);
		} finally {
			//构建文档路径
			String baseDir = document.getContent();
			Path docPath = Paths.get(baseDir);
			File docfile = new File(String.valueOf(docPath));
			if (docfile.exists()) {
				docfile.delete();
			}
		}
		return ResponseResult.SUCCESS(GlobalTipMsg.DOCUMENT_DELETE_SUCCESS);
	}

	@Override
	public ResponseResult updateDocument(String menuId, Document document) throws IOException {

		Document oldDocument = documentDao.findDocumentByMenuId(menuId);
		oldDocument.setTitle(document.getTitle());
		File file = new File(baseDir);
		Path dir = Paths.get(baseDir);
		if (!file.exists()) {
			Files.createDirectory(dir);
		}
		//删除旧数据
		File oldFile = new File(oldDocument.getContent());
		if (oldFile.exists()) {
			FileUtils.forceDelete(oldFile);
		}
		File documentFile = createDocumentFile(document, baseDir);
		if (documentFile == null) {
			log.error("新修改的文件写入磁盘失败！");
			return ResponseResult.FAILED(GlobalTipMsg.DOCUMENT_SAVE_FAILED);
		}
		//设置doucument的内容为html的路径
		oldDocument.setContent(String.valueOf(documentFile.toPath()));
		//保存
		Menu menu = menuDao.findMenuById(oldDocument.getMenuId());
		menu.setName(document.getTitle());
		try {
			menuDao.save(menu);
			documentDao.save(oldDocument);
		} catch (Exception e) {
			FileUtils.forceDelete(documentFile);
			log.error("新修改的文档保存到数据库失败！");
			return ResponseResult.FAILED(GlobalTipMsg.DOCUMENT_SAVE_FAILED);
		}
		return ResponseResult.SUCCESS(GlobalTipMsg.DOCUMENT_UPADATE_SUCCESS);
	}

	@Override
	public ResponseResult getDocDetail(String menuId) {
		Menu docMenu = menuDao.findMenuById(menuId);
		if (docMenu == null) {
			return ResponseResult.FAILED(GlobalTipMsg.DOCUMENT_NOT_EXISTS);
		}
		Document document = documentDao.findDocumentByMenuId(menuId);
		String docPath = document.getContent();
		Path path = Paths.get(docPath);
		Document completeDoc = new Document();
		completeDoc.setTitle(document.getTitle());
		completeDoc.setUpdateTime(document.getUpdateTime());
		completeDoc.setCreateTime(document.getCreateTime());
		completeDoc.setMenuId(document.getMenuId());

		try {
			String html = FileUtils.readFileToString(path.toFile(), "UTF-8");
			completeDoc.setContent(html);
		} catch (IOException e) {
			log.error(e.toString());
			return ResponseResult.FAILED(GlobalTipMsg.DOCUMENT_QUERY_FAILED);
		}
		return ResponseResult.SUCCESS().setData(completeDoc);
	}

	@Override
	public void reportDocument(String documentId, String type, HttpServletResponse response) throws IOException {
		Document document = documentService.getDocument(documentId);
		String fileName = document.getTitle();
		String content = document.getContent();
		String htmlStr = content.replaceAll("<html><head>", "<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>" + fileName + "</title>");
		//临时文件夹有PDF，但是为了更为优雅的存取，再保存一遍
		String baseDir = PropertiesUtil.getProp("report.pdf-dir");
		Path dir = Paths.get(baseDir);
		File file = new File(baseDir);
		if (!file.exists()) {
			Files.createDirectory(dir);
		}
		//读取并生成PDF
		FileItem pdf = HtmlToPDFUtils.build().convertPDFFromText(htmlStr, document.getTitle());
		InputStream in = pdf.getInputStream();
		byte[] bytes = IOUtils.toByteArray(in);
		Files.write(Paths.get(baseDir, fileName + ".pdf"), bytes);
		//读取生成的PDF输出到response
		FileInputStream is = new FileInputStream(Paths.get(baseDir, fileName + ".pdf").toFile());
		response.setCharacterEncoding("UTF-8");
		response.setContentType("application/pdf");
		String pdfName = fileName + ".pdf";
		response.setHeader("Content-Disposition", "attachment; filename=\"" + pdfName + "\"");
		OutputStream os = response.getOutputStream();
		os.write(IOUtils.toByteArray(is));
		is.close();
		in.close();
		os.close();
		//输出完毕删除生成的文件
		try {
			Thread.sleep(1000);
			String tempDir = PropertiesUtil.getProp("html.pdf-temp");
				FileUtils.forceDelete(Paths.get(baseDir, pdfName).toFile());
				FileUtils.forceDelete(Paths.get(tempDir, pdfName).toFile());
				FileUtils.forceDelete(Paths.get(tempDir, fileName + ".html").toFile());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

	@Override
	public Document getDocument(String menuId) {
		Menu docMenu = menuDao.findMenuById(menuId);
		if (docMenu == null) {
			return null;
		}
		Document document = documentDao.findDocumentByMenuId(menuId);
		String docPath = document.getContent();
		Path path = Paths.get(docPath);
		ObjectMapper mapper = new ObjectMapper();
		Document completeDoc = null;
		try {
			completeDoc = mapper.readValue(mapper.writeValueAsString(document), Document.class);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		String docDetail = readDocHtml(path);
		completeDoc.setContent(docDetail);
		return completeDoc;
	}

	@Override
	public ResponseResult moveDocument(String documentId, String menuId) {
		//查询文件
		Document document = documentService.getDocument(documentId);
		if (document == null) {
			return ResponseResult.FAILED(GlobalTipMsg.DOCUMENT_NOT_EXISTS);
		}
		//更改节点的parentId
		Menu menu = menuDao.findMenuById(documentId);
		menu.setParentId(menuId);
		menuDao.save(menu);
		return ResponseResult.SUCCESS(GlobalTipMsg.DOCUMENT_MOVE_SUCCESS);
	}

	@Override
	public ResponseResult copyDocument(String menuId, String documentId) throws IOException {
		//查询需要复制文档
		Document document = documentDao.findDocumentByMenuId(documentId);
		if (document == null) {
			return ResponseResult.FAILED(GlobalTipMsg.DOCUMENT_NOT_EXISTS);
		}
		//深拷贝文档
		ObjectMapper mapper = new ObjectMapper();
		Document completeDoc = mapper.readValue(mapper.writeValueAsString(document), Document.class);
		//获取文档所在的真实路径
		String docPath = document.getContent();
		//生成路径
		Path path = Paths.get(docPath);
		//查询文档的具体详情
		String docDetail = readDocHtml(path);

		//开始复制文档
		//复制文档节点
		Menu menu = menuDao.findMenuById(documentId);
		Menu newMenu = mapper.readValue(mapper.writeValueAsString(menu), Menu.class);
		//新的文档节点设置
		newMenu.setParentId(menuId);
		//新的的文档ID
		String newId = String.valueOf(idGenerator.nextId());
		newMenu.setId(newId);
		completeDoc.setMenuId(newId);
		completeDoc.setContent(docDetail);
		//在同一目录下复制
		//校验是否有重名文档，如果有重名或者在同一目录下复制，加后缀
		List<Menu> menus = menuDao.findMenusByParentIdAndName(menuId, document.getTitle());
		if (menuId.equals(menu.getParentId()) || menus.size() > 0) {
			String suffix = Utils.generateSuffix();
			String newName = menu.getName() + "_" + suffix;
			newMenu.setName(newName);
			completeDoc.setTitle(newName);
		}
		//将新的文档保存到文件，修改内容为文件位置
		//获取保存文件的路径
		File file = new File(baseDir);
		Path dir = Paths.get(baseDir);
		if (!file.exists()) {
			Files.createDirectory(dir);
		}
		File documentFile = createDocumentFile(completeDoc, baseDir);
		if (documentFile == null) {
			log.error("复制的文件写入磁盘失败！");
			return ResponseResult.FAILED(GlobalTipMsg.DOCUMENT_COPY_EXCEPTION);
		}

		//更新复制的文档节点文件路径
		completeDoc.setContent(documentFile.toPath().toString());
		//保存
		try {
			menuDao.save(newMenu);
			documentDao.save(completeDoc);
		} catch (Exception e) {
			log.error("复制的文档保存到数据库失败！");
			FileUtils.forceDelete(documentFile);
			return ResponseResult.FAILED(GlobalTipMsg.DOCUMENT_SAVE_FAILED);
		}
		return ResponseResult.SUCCESS(GlobalTipMsg.DOCUMENT_COPY_SUCCESS);
	}

	/**
	 * jsoup解析拼接html,生成pdf导出
	 *
	 * @param documentIds
	 * @param type
	 * @param response
	 * @param fileName
	 */
	@Override
	public void reportDocuments(String[] documentIds, String type, HttpServletResponse response, String fileName) throws IOException {
		// 查询所有文档
		List<String> ids = new ArrayList<>();
		for (String documentId : documentIds) {
			ids.add(documentId);
		}
		// 叠加多个body内容
		List<String> documents = documentDao.findDocumentsByIds(ids);
		String[] docHtmlDir = Utils.convertList(documents);
		for (int i = 0; i < docHtmlDir.length; i++) {
			String docHtml = readDocHtml(Paths.get(docHtmlDir[i]));
			org.jsoup.nodes.Document document = Jsoup.parse(docHtml);
			docHtmlDir[i] = document.body().toString();
		}
		//拼接body构建html
		String[] docHtml = docHtmlDir;
		String htmlPrefix = "<!DOCTYPE html PUBLIC \\\"-//W3C//DTD XHTML 1.0 Transitional//EN\\\" \\\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\\\"><html><head><style>p{margin-top:0pt;margin-bottom:1pt;}p.a{text-align:justify;}</style><meta http-equiv=\\\"Content-Type\\\" content=\\\"text/html;charset=UTF-8\\\"></meta><title>" + fileName + "</title></head><body>";
		String htmlSuffix = "</body></html>";
		StringBuilder htmlStr = new StringBuilder(htmlPrefix);
		for (int i = 0; i < docHtml.length; i++) {
			htmlStr.append(docHtml[i]);
		}
		htmlStr.append(htmlSuffix);
		//临时文件夹有PDF，但是为了更为优雅的存取，再保存一遍
		String baseDir = PropertiesUtil.getProp("report.pdf-dir");
		Path dir = Paths.get(baseDir);
		File file = new File(baseDir);
		if (!file.exists()) {
			Files.createDirectory(dir);
		}
		//读取并生成PDF
		FileItem pdf = HtmlToPDFUtils.build().convertPDFFromText(htmlStr.toString(), fileName);
		InputStream in = pdf.getInputStream();
		byte[] bytes = IOUtils.toByteArray(in);
		Files.write(Paths.get(baseDir, fileName + ".pdf"), bytes);
		//读取生成的PDF输出到response
		FileInputStream is = new FileInputStream(Paths.get(baseDir, fileName + ".pdf").toFile());
		response.setCharacterEncoding("UTF-8");
		response.setContentType("application/pdf");
		String pdfName = fileName + ".pdf";
		response.setHeader("Content-Disposition", "attachment; filename=\"" + pdfName + "\"");
		OutputStream os = response.getOutputStream();
		os.write(IOUtils.toByteArray(is));
		is.close();
		in.close();
		os.close();
		//输出完毕删除生成的文件
		try {
			Thread.sleep(1000);
			String tempDir = PropertiesUtil.getProp("html.pdf-temp");
				FileUtils.forceDelete(Paths.get(baseDir, pdfName).toFile());
				FileUtils.forceDelete(Paths.get(tempDir, pdfName).toFile());
				FileUtils.forceDelete(Paths.get(tempDir, fileName + ".html").toFile());
		} catch (InterruptedException e) {
			e.printStackTrace();

	}
	}

	/**
	 * 读取路径下的html文件
	 *
	 * @param path
	 * @return
	 */
	private String readDocHtml(Path path) {
		try {
			return FileUtils.readFileToString(path.toFile(), "UTF-8");
		} catch (IOException e) {
			log.error("Failed to read html：" + e.toString());
			return null;
		}
	}
}
